#include "mesh_manager.h"
#include "mesh_loader.h"
#include "mesh.h"
#include "thread.h"

#include <iostream>
#include <list>

using namespace krryn::scene;
using namespace krryn::core;

namespace krryn{
namespace scene{
	class mesh_queue_entry{
	public:
		std::string m_Name;
		std::string m_Filename;
		bool m_Finished;

		mesh_queue_entry(const std::string &a_Name, const std::string &a_Filename)
			: m_Name(a_Name), m_Filename(a_Filename), m_Finished(false) {}
	};
}
}

class mesh_manager_thread : public thread{
public:
	mesh_manager_thread(mesh_loader *a_MeshLoader, mesh_queue_entry *a_Entry)
		: m_MeshLoader(a_MeshLoader), m_Mesh(0), m_Entry(a_Entry) {}

	~mesh_manager_thread(){
		delete m_MeshLoader; m_MeshLoader = 0;
		delete m_Entry; m_Entry = 0;
	}

	virtual void run(){
		m_Mesh = m_MeshLoader->load(m_Entry->m_Filename);
		if(m_Mesh) m_Mesh->set_name(m_Entry->m_Name);
		m_Entry->m_Finished = true;
	}

	mesh_queue_entry *get_entry(){
		return m_Entry;
	}

	mesh *get_mesh(){
		return m_Mesh;
	}

	bool is_done(){
		return m_Entry->m_Finished;
	}

	const std::string &filename(){
		return m_Entry->m_Filename;
	}

private:
	mesh_loader *m_MeshLoader;
	mesh *m_Mesh;
	mesh_queue_entry *m_Entry;
};

void mesh_manager::subscribe(mesh_loader *a_MeshLoader){
	m_Exts[a_MeshLoader->file_ext()] = a_MeshLoader;
}

void mesh_manager::queue(const std::string &a_Name, const std::string &a_Filename){
	m_Queue.push(new mesh_queue_entry(a_Name, a_Filename));
}

mesh* mesh_manager::get_mesh_by_name(const std::string &a_Name){
	std::map<std::string, mesh*>::iterator i = m_Done.find(a_Name);
	if(i != m_Done.end()){
		return (*i).second;
	}else{
		std::clog << "Warning: cannot find model '" << a_Name << "'" << std::endl;
		return 0;
	}
}

void mesh_manager::load_all(){
	std::list<mesh_manager_thread*> m_Pool;

	// fire up thread to load the models
	while(!m_Queue.empty()){
		mesh_queue_entry *i = m_Queue.front();

		size_t l_Pos = i->m_Filename.find_last_of('.');
		if(l_Pos != std::string::npos){
			std::string l_Ext = i->m_Filename.substr(l_Pos + 1); // + 1 skips the '.'

			if(m_Exts.find(l_Ext) != m_Exts.end()){
				std::clog << "Loading '." << l_Ext << "' file '" << i->m_Filename << "'" << std::endl;

				mesh_manager_thread *l_Thread = new mesh_manager_thread(m_Exts[l_Ext]->clone(), i);
				l_Thread->start();
				m_Pool.push_back(l_Thread);
			}else{
				std::clog << "Error: unsupported file extension '" << l_Ext << "'" << std::endl;
			}
		}else{
			std::clog << "Error: no file extension found for " << i->m_Filename << std::endl;
		}
		m_Queue.pop();
	}

	// wait until all the models have been loaded
	while(!m_Pool.empty()){
		for(std::list<mesh_manager_thread*>::iterator i = m_Pool.begin(); i != m_Pool.end();){
			if((*i)->is_done()){
				if((*i)->get_mesh() != 0){
					m_Done[(*i)->get_entry()->m_Name] = (*i)->get_mesh();
					std::clog << "Done loading file '" << (*i)->filename() << "'" << std::endl;
				}else{
					std::clog << "Error loading file '" << (*i)->filename() << "'" << std::endl;
				}

				// clean up
				(*i)->kill();
				delete *i, *i = 0;

				// erase advances iterator
				i = m_Pool.erase(i);
			}else{
				// only advance if we didn't delete anything
				i++;
			}
		}
	}
}
